---
title: 语法对比基础
---

本模块探讨 JavaScript 和 Go 之间的基本语法差异，涵盖变量声明、控制流、函数、数据类型和其他核心语言结构。

## 变量声明和类型系统

JavaScript 使用动态类型，变量类型在运行时确定，而 Go 使用静态类型，具有类型推断能力。

### 变量声明方式对比

| 声明方式 | JavaScript | Go | 说明 |
| :------- | :---------- | :-- | :--- |
| **变量声明** | `let x = 10;` | `x := 10` | 类型推断，局部作用域 |
| **常量声明** | `const x = 10;` | `const x = 10` | 不可重新赋值 |
| **显式类型声明** | `let x: number = 10;` (TypeScript) | `var x int = 10` | 明确指定类型 |
| **零值初始化** | `let x;` (undefined) | `var x int` (0) | 默认值初始化 |
| **多重赋值** | `let a = 1, b = 2;` | `a, b := 1, 2` | 同时声明多个变量 |
| **块级作用域** | `{ let x = 10; }` | `{ x := 10 }` | 限制变量作用域 |
| **函数作用域** | `var x = 10;` | `var x = 10` | 提升到函数顶部 |

### 类型推断对比

| 特性 | JavaScript | Go | 说明 |
| :---- | :---------- | :-- | :--- |
| **数字类型** | `number` (64位浮点) | `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `float32`, `float64` | Go 有精确的整数类型 |
| **字符串类型** | `string` | `string` | 都支持 UTF-8 |
| **布尔类型** | `boolean` | `bool` | 相同概念 |
| **数组类型** | `Array` (动态) | `[n]T` (固定), `[]T` (切片) | Go 区分数组和切片 |
| **对象类型** | `Object` | `struct`, `map` | Go 使用结构体作为主要数据结构 |
| **空值** | `null`, `undefined` | `nil` | Go 统一使用 nil |
| **类型检查** | 运行时 | 编译时 | Go 编译时类型安全 |

<UniversalEditor title="变量声明和赋值对比" compare={true}>
```javascript !! js
// JavaScript: 动态类型
let age = 30; // Number
let name = "Alice"; // String
let isActive = true; // Boolean
let scores = [85, 92, 78]; // Array
let person = { name: "Bob", age: 25 }; // Object

console.log(typeof age); // "number"
console.log(typeof name); // "string"
console.log(Array.isArray(scores)); // true
```

```go !! go
// Go: 静态类型与类型推断
package main

import "fmt"

func main() {
    age := 30 // int (类型推断)
    name := "Alice" // string (类型推断)
    isActive := true // bool (类型推断)
    scores := []int{85, 92, 78} // int 切片
    person := map[string]interface{}{"name": "Bob", "age": 25} // map
    
    // Go 没有像 JavaScript 那样的 typeof，但可以使用反射
    fmt.Printf("age 类型: %T\n", age) // int
    fmt.Printf("name 类型: %T\n", name) // string
    fmt.Printf("scores 类型: %T\n", scores) // []int
}
```
</UniversalEditor>

## 控制流语句对比

两种语言都支持类似的控制流结构，但语法和约定不同。

### If/Else 语句

<UniversalEditor title="If/Else 对比" compare={true}>
```javascript !! js
// JavaScript: If/Else
let temperature = 25;

if (temperature > 30) {
    console.log("天气很热！");
} else if (temperature > 20) {
    console.log("天气温暖。");
} else {
    console.log("天气寒冷。");
}

// JavaScript 允许真值/假值
if (temperature) {
    console.log("温度是真值");
}
```

```go !! go
// Go: If/Else
package main

import "fmt"

func main() {
    temperature := 25

    if temperature > 30 {
        fmt.Println("天气很热！")
    } else if temperature > 20 {
        fmt.Println("天气温暖。")
    } else {
        fmt.Println("天气寒冷。")
    }

    // Go 需要显式的布尔表达式
    if temperature > 0 {
        fmt.Println("温度是正数")
    }
}
```
</UniversalEditor>

### For 循环

<UniversalEditor title="For 循环对比" compare={true}>
```javascript !! js
// JavaScript: For 循环
// 传统 for 循环
for (let i = 0; i < 5; i++) {
    console.log(i);
}

// For...of (用于可迭代对象)
let arr = [1, 2, 3, 4, 5];
for (let val of arr) {
    console.log(val);
}

// For...in (用于对象属性)
let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
    console.log(key, obj[key]);
}

// forEach 方法
arr.forEach((val, index) => {
    console.log(`索引 ${index}: ${val}`);
});
```

```go !! go
// Go: For 循环
package main

import "fmt"

func main() {
    // 传统 for 循环（Go 的唯一循环结构）
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }

    // 基于 range 的 for 循环（类似 for...of）
    arr := []int{1, 2, 3, 4, 5}
    for i, val := range arr {
        fmt.Printf("索引 %d: %d\n", i, val)
    }

    // 遍历 map（类似 for...in）
    obj := map[string]int{"a": 1, "b": 2, "c": 3}
    for key, val := range obj {
        fmt.Printf("%s: %d\n", key, val)
    }

    // 遍历切片（只要索引）
    for i := range arr {
        fmt.Printf("索引: %d\n", i)
    }

    // 遍历切片（只要值）
    for _, val := range arr {
        fmt.Printf("值: %d\n", val)
    }
}
```
</UniversalEditor>

## 函数定义和调用

Go 函数需要显式的返回类型，与 JavaScript 函数的语法不同。

<UniversalEditor title="函数定义对比" compare={true}>
```javascript !! js
// JavaScript: 函数定义
// 函数声明
function greet(name) {
    return `Hello, ${name}!`;
}

// 函数表达式
const add = function(a, b) {
    return a + b;
};

// 箭头函数
const multiply = (a, b) => a * b;

// 带默认参数的函数
function greetWithDefault(name = "World") {
    return `Hello, ${name}!`;
}

// 带剩余参数的函数
function sum(...numbers) {
    return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(greet("Bob"));
console.log(add(5, 3));
console.log(multiply(4, 6));
console.log(greetWithDefault());
console.log(sum(1, 2, 3, 4, 5));
```

```go !! go
// Go: 函数定义
package main

import "fmt"

// 函数声明
func greet(name string) string {
    return fmt.Sprintf("Hello, %s!", name)
}

// 带多个返回值的函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除零错误")
    }
    return a / b, nil
}

// 带命名返回值的函数
func multiply(a, b int) (result int) {
    result = a * b
    return // 裸返回
}

// 带可变参数的函数（类似剩余参数）
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

// 函数作为变量（类似函数表达式）
var add = func(a, b int) int {
    return a + b
}

func main() {
    fmt.Println(greet("Bob"))
    
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("结果:", result)
    }
    
    fmt.Println(multiply(4, 6))
    fmt.Println(sum(1, 2, 3, 4, 5))
    fmt.Println(add(5, 3))
}
```
</UniversalEditor>

## 数据类型和结构

Go 有丰富的内置类型和结构，与 JavaScript 有显著差异。

### 数据类型详细对比

| 数据类型 | JavaScript | Go | 零值 | 说明 |
| :------- | :---------- | :-- | :--- | :--- |
| **整数** | `number` | `int`, `int8`, `int16`, `int32`, `int64` | `0` | Go 有精确的整数大小 |
| **无符号整数** | 无 | `uint`, `uint8`, `uint16`, `uint32`, `uint64` | `0` | Go 支持无符号整数 |
| **浮点数** | `number` | `float32`, `float64` | `0.0` | Go 区分单双精度 |
| **复数** | 无 | `complex64`, `complex128` | `0+0i` | Go 内置复数支持 |
| **字符串** | `string` | `string` | `""` | 都支持 UTF-8 编码 |
| **布尔值** | `boolean` | `bool` | `false` | 相同概念 |
| **空值** | `null`, `undefined` | `nil` | `nil` | Go 统一使用 nil |
| **数组** | `Array` | `[n]T` | `[0,0,0]` | Go 数组固定大小 |
| **动态数组** | `Array` | `[]T` (切片) | `nil` | Go 切片是动态的 |
| **对象** | `Object` | `struct` | 结构体零值 | Go 结构体有定义的结构 |
| **映射** | `Map` | `map[K]V` | `nil` | 键值对存储 |
| **集合** | `Set` | 无内置 | - | Go 可用 map 模拟 |
| **函数** | `Function` | `func` | `nil` | 函数类型 |
| **通道** | 无 | `chan T` | `nil` | Go 并发通信 |
| **接口** | 无 | `interface{}` | `nil` | Go 多态支持 |
| **指针** | 无 | `*T` | `nil` | Go 内存地址引用 |

<UniversalEditor title="数据类型对比" compare={true}>
```javascript !! js
// JavaScript: 数据类型和结构
// 基本类型
let number = 42;
let string = "Hello";
let boolean = true;
let nullValue = null;
let undefinedValue = undefined;

// 数组
let array = [1, 2, 3, 4, 5];
let mixedArray = [1, "hello", true, null];

// 对象
let object = {
    name: "Alice",
    age: 30,
    isActive: true
};

// Map (ES6)
let map = new Map();
map.set("key1", "value1");
map.set("key2", "value2");

// Set (ES6)
let set = new Set([1, 2, 3, 3, 4]); // 重复项被移除

console.log(array.length);
console.log(Object.keys(object));
console.log(map.get("key1"));
console.log(set.has(3));
```

```go !! go
// Go: 数据类型和结构
package main

import "fmt"

func main() {
    // 基本类型
    var number int = 42
    var text string = "Hello"
    var boolean bool = true
    
    // 数组（固定大小）
    var array [5]int = [5]int{1, 2, 3, 4, 5}
    // [4]interface{} 表示一个长度固定为 4 的数组，元素类型为 interface{}（可以容纳任意类型）。
    var mixedArray [4]interface{} = [4]interface{}{1, "hello", true, nil}
    
    // 切片（动态数组）
    slice := []int{1, 2, 3, 4, 5}
    slice = append(slice, 6) // 添加元素
    
    // Map
    // map[string]interface{} // Go 的内建映射类型（类似于 JS 的对象或 Map）。
    // string // 表示 key 必须是字符串类型。
    // interface{} // 表示 value 可以是任意类型（因为 interface{} 是空接口，任何类型都实现了它）。
    object := map[string]interface{}{
        "name":     "Alice",
        "age":      30,
        "isActive": true,
    }
    
    // 结构体（类似对象但有定义的结构）
    type Person struct {
        Name     string
        Age      int
        IsActive bool
    }
    
    person := Person{
        Name:     "Bob",
        Age:      25,
        IsActive: true,
    }
    
    fmt.Printf("数组长度: %d\n", len(array))
    fmt.Printf("切片长度: %d\n", len(slice))
    fmt.Printf("Map 键: %v\n", getMapKeys(object))
    fmt.Printf("Person: %+v\n", person)
}

// 辅助函数获取 map 键
func getMapKeys(m map[string]interface{}) []string {
    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}
```
</UniversalEditor>

## 作用域和生命周期

两种语言都有块作用域，但 Go 的作用域规则更显式和一致。

<UniversalEditor title="作用域和生命周期对比" compare={true}>
```javascript !! js
// JavaScript: 作用域和提升
function example() {
    let x = 10; // 块作用域
    
    if (true) {
        let y = 20; // 块作用域
        var z = 30; // 函数作用域（提升）
        console.log(x); // 10
        console.log(y); // 20
        console.log(z); // 30
    }
    
    console.log(x); // 10
    console.log(z); // 30（由于提升可访问）
    // console.log(y); // ReferenceError: y is not defined
}

// 闭包
function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
```

```go !! go
// Go: 作用域和生命周期
package main

import "fmt"

func example() {
    x := 10 // 函数作用域
    
    if true {
        y := 20 // 块作用域
        fmt.Println(x) // 10
        fmt.Println(y) // 20
    }
    
    fmt.Println(x) // 10
    // fmt.Println(y) // Error: undefined: y
}

// Go 中的闭包
func createCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    example()
    
    counter := createCounter()
    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2
}
```
</UniversalEditor>

## 错误处理

JavaScript 使用 try-catch 进行异常处理，而 Go 使用显式的错误返回值。

<UniversalEditor title="错误处理对比" compare={true}>
```javascript !! js
// JavaScript: 异常处理
function divide(a, b) {
    if (b === 0) {
        throw new Error("除零错误");
    }
    return a / b;
}

function safeDivide(a, b) {
    try {
        const result = divide(a, b);
        console.log("结果:", result);
        return result;
    } catch (error) {
        console.error("错误:", error.message);
        return null;
    }
}

// 异步错误处理
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error(`HTTP 错误! 状态: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("获取错误:", error.message);
        throw error; // 重新抛出
    }
}

safeDivide(10, 2); // 结果: 5
safeDivide(10, 0); // 错误: 除零错误
```

```go !! go
// Go: 错误处理
package main

import (
    "errors"
    "fmt"
    "net/http"
    "io/ioutil"
)

// 返回错误的函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除零错误")
    }
    return a / b, nil
}

// 处理错误的函数
func safeDivide(a, b float64) {
    result, err := divide(a, b)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        return
    }
    fmt.Printf("结果: %v\n", result)
}

// 自定义错误类型
type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("验证错误 %s: %s", e.Field, e.Message)
}

// 带自定义错误的函数
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{Field: "age", Message: "年龄不能为负数"}
    }
    if age > 150 {
        return ValidationError{Field: "age", Message: "年龄似乎不现实"}
    }
    return nil
}

// 带错误处理的 HTTP 请求
func fetchData(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, fmt.Errorf("获取数据失败: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("HTTP 错误! 状态: %d", resp.StatusCode)
    }
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("读取响应失败: %w", err)
    }
    
    return body, nil
}

func main() {
    safeDivide(10, 2) // 结果: 5
    safeDivide(10, 0) // 错误: 除零错误
    
    if err := validateAge(-5); err != nil {
        fmt.Printf("验证错误: %v\n", err)
    }
    
    if err := validateAge(200); err != nil {
        fmt.Printf("验证错误: %v\n", err)
    }
}
```
</UniversalEditor>

## 变量作用域对比

JavaScript 和 Go 在变量作用域方面有一些重要差异。

### 作用域规则对比

| 作用域类型 | JavaScript | Go | 说明 |
| :--------- | :---------- | :-- | :--- |
| **全局作用域** | `var x = 10;` | `var x = 10` (包级别) | 都支持全局变量 |
| **函数作用域** | `function() { var x = 10; }` | `func() { x := 10 }` | 函数内部变量 |
| **块级作用域** | `{ let x = 10; }` | `{ x := 10 }` | 花括号内变量 |
| **提升行为** | `var` 提升到函数顶部 | 无提升 | Go 变量必须在使用前声明 |
| **闭包支持** | 完全支持 | 完全支持 | 都支持函数闭包 |
| **变量遮蔽** | 支持 | 支持 | 内层变量遮蔽外层 |
| **循环变量** | `for (let i = 0; ...)` | `for i := 0; ...` | 循环计数器作用域 |

### 变量生命周期对比

| 生命周期特性 | JavaScript | Go | 说明 |
| :----------- | :---------- | :-- | :--- |
| **垃圾回收** | 自动 | 自动 | 都使用垃圾回收 |
| **内存管理** | 自动 | 自动 | 开发者无需手动管理 |
| **变量销毁** | 作用域结束时 | 作用域结束时 | 自动清理 |
| **引用计数** | 自动 | 自动 | 引用计数管理 |
| **内存泄漏** | 闭包可能导致 | 闭包可能导致 | 需要注意循环引用 |

## 基本数据类型对比

Go 有全面的内置类型，每种类型都有特定用途。

| JavaScript 类型 | Go 等价类型 | 说明 |
| :-------------- | :----------- | :--- |
| `number`        | `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `float32`, `float64`, `complex64`, `complex128` | Go 有特定的整数大小 |
| `string`        | `string` | 默认 UTF-8 编码 |
| `boolean`       | `bool` | 相同概念 |
| `null`          | `nil` | 用于指针、接口、切片、map、通道 |
| `undefined`     | 无直接等价 | 通常用 `nil` 或零值表示 |
| `object`        | `struct`, `map[string]interface{}` | 结构体是分组数据的主要方式 |
| `array`         | `[n]T` (数组), `[]T` (切片) | 数组固定大小，切片动态 |
| `symbol`        | 无直接等价 | Go 类型系统中不需要 |
| `bigint`        | `int64`, `uint64`, 或外部包 | 用于任意精度 |

## 运算符和表达式

大多数运算符相似，但 Go 在语法和行为上有一些差异。

<UniversalEditor title="运算符对比" compare={true}>
```javascript !! js
// JavaScript: 运算符
let a = 10, b = 3;

// 算术
let sum = a + b; // 13
let difference = a - b; // 7
let product = a * b; // 30
let quotient = a / b; // 3.333...
let remainder = a % b; // 1
let power = a ** b; // 1000

// 比较
let isEqual = (a === b); // false (严格相等)
let isLooseEqual = (a == b); // false (宽松相等)
let isGreater = (a > b); // true
let isLess = (a < b); // false

// 逻辑
let and = true && false; // false
let or = true || false; // true
let not = !true; // false

// 位运算
let bitwiseAnd = a & b; // 2
let bitwiseOr = a | b; // 11
let bitwiseXor = a ^ b; // 9
let leftShift = a << 1; // 20
let rightShift = a >> 1; // 5

console.log(sum, difference, product, quotient, remainder, power);
```

```go !! go
// Go: 运算符
package main

import (
    "fmt"
    "math"
)

func main() {
    a, b := 10, 3

    // 算术
    sum := a + b // 13
    difference := a - b // 7
    product := a * b // 30
    quotient := float64(a) / float64(b) // 3.333... (需要浮点转换)
    remainder := a % b // 1
    power := math.Pow(float64(a), float64(b)) // 1000 (需要 math 包)

    // 比较
    isEqual := (a == b) // false
    isGreater := (a > b) // true
    isLess := (a < b) // false

    // 逻辑
    and := true && false // false
    or := true || false // true
    not := !true // false

    // 位运算
    bitwiseAnd := a & b // 2
    bitwiseOr := a | b // 11
    bitwiseXor := a ^ b // 9
    leftShift := a << 1 // 20
    rightShift := a >> 1 // 5

    fmt.Printf("算术: %d, %d, %d, %.2f, %d, %.0f\n", 
        sum, difference, product, quotient, remainder, power)
    fmt.Printf("比较: %t, %t, %t\n", isEqual, isGreater, isLess)
    fmt.Printf("逻辑: %t, %t, %t\n", and, or, not)
    fmt.Printf("位运算: %d, %d, %d, %d, %d\n", 
        bitwiseAnd, bitwiseOr, bitwiseXor, leftShift, rightShift)
}
```
</UniversalEditor>

---

### 练习题：
1. 解释 Go 中 `array` 和 `slice` 类型的区别，以及何时使用每种类型。
2. 编写一个 Go 函数，接受整数切片并返回总和和平均值，演示多个返回值。
3. Go 的错误处理与 JavaScript 的异常处理有何不同？提供两种方法的示例。
4. 创建一个 Go 结构体来表示 `Person`，包含姓名、年龄和邮箱字段，然后编写一个函数来验证人员数据。

### 项目想法：
* 在 Go 中构建一个简单的计算器程序，可以执行基本算术运算，演示函数使用、错误处理和用户输入处理。与 JavaScript 等效版本进行比较。

### 下一步：
* 学习 Go 的包系统和模块管理
* 理解 Go 的类型系统和接口
* 探索 Go 强大的并发特性：goroutines 和 channels
